// Includes
#include "GameObject.h"
#include "MeshLoaders.h"		// Mesh loader header

#include <cstring>		// C string library
#include <cctype>		// C library character identification
#include <iostream>		// C++ I/O stream library

#ifdef _WIN32
#include <windows.h>		// Include windows header on the windows platform
#endif
#include <GL/gl.h>		// OpenGL header

using namespace std;

namespace EraAstral
{
	namespace graphics
	{
		// Game object default constructor
		GameObject::GameObject()
		{
			// Set null pointers
			m_mesh = NULL;
			
			// Set default world values
			m_position = new Vector3();
			m_rotation = new Vector3();
			m_scale = new Vector3(1.0f);
		}
		
		// Game object default destructor
		GameObject::~GameObject()
		{
			// Delete mesh
			if (m_mesh != NULL)
				delete m_mesh;
				
			// Delete position, rotation and scale
			delete m_position;
			delete m_rotation;
			delete m_scale;
		}
		
		// Loads a mesh for the game object
		void GameObject::LoadMesh(const char *fileName)
		{
			// Check if a mesh already exists
			if (m_mesh != NULL)
			{
				// Delete current mesh
				delete m_mesh;
				m_mesh = NULL;
			}
		
			// Determine extension of the file
			int dotPosition = -1;
			char *lowerCaseFileName = new char[strlen(fileName) + 1];
			// Add string end symbol
			lowerCaseFileName[strlen(fileName)] = '\0';
			// Loop through to find dot
			for (unsigned int i = 0; i < strlen(fileName); i++)
			{
				// Convert to lower case letters
				lowerCaseFileName[i] = tolower(fileName[i]);	
			
				if (fileName[i] == '.')
					dotPosition = i;
			}
			
			// Check if no dot was found
			if (dotPosition == -1)
			{
				cout << "Error loading mesh file. No file extension found." << endl;
				// Free memory used for lower case file name
				delete[] lowerCaseFileName;
				return;
			}
			
			// Check extension
			if (strncmp(lowerCaseFileName + dotPosition, ".obj", 4) == 0)
			{
				cout << "Mesh file identified as Wavefront OBJ." << endl;
				// Load Wavefront OBJ file
				m_mesh = LoadOBJ(fileName);
			}
			else
			{
				// Unknown mesh file format
				cout << "Error loading mesh: File format not recognized." << endl;
			}
				
			// TODO: Add more mesh loader functions
			
			// Check if mesh was loaded successfully
			if (m_mesh == NULL)
			{
				// Error while loading
				cout << "Could not load mesh from file " << fileName << endl;
			}
				
			// Free memory used for lower case file name
			delete[] lowerCaseFileName;
		}
		
		// Draws the game object
		void GameObject::Draw()
		{
			// Check if mesh is null
			if (m_mesh == NULL)
				return;
				
			// Push temporary matrix on to the stack
			glPushMatrix();				
				
			// Apply transforms
			// Translation
			glTranslatef(m_position->X, m_position->Y, m_position->Z);
			// Rotation
			glRotatef(m_rotation->Y, 0.0f, 1.0f, 0.0f);		// Yaw
			glRotatef(m_rotation->X, 1.0f, 0.0f, 0.0f);		// Pitch
			glRotatef(m_rotation->Z, 0.0f, 0.0f, 1.0f);		// Roll
			// Scaling
			glScalef(m_scale->X, m_scale->Y, m_scale->Z);
			
			// Draw mesh
			m_mesh->Draw();
			
			// Pop temporary matrix off the stack
			glPopMatrix();
		}
		
		// Gets the position of the game object
		Vector3 *GameObject::GetPosition() const
		{
			return m_position;
		}
		
		// Sets the position of the game object
		void GameObject::SetPosition(const Vector3& position)
		{
			m_position->Set(position);
		}
		
		// Gets the rotation of the game object
		Vector3 *GameObject::GetRotation() const
		{
			return m_rotation;
		}
		
		// Sets the rotation of the game object
		void GameObject::SetRotation(const Vector3& rotation)
		{
			m_rotation->Set(rotation);
		}
		
		// Gets the scale of the game object
		Vector3 *GameObject::GetScale() const
		{
			return m_scale;
		}
		
		// Sets the scale of the game object
		void GameObject::SetScale(const Vector3& scale)
		{
			m_scale->Set(scale);
		}
		
		// Gets the mesh of the game object
		IMesh *GameObject::GetMesh() const
		{
			return m_mesh;
		}
	}
}
